﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FreeAccessNetdisk.src.classes
{
    public class FileWatcher : IWatcher
    {
        private string targetDir = null;//监控目标文件夹,即本地同步区文件夹
        private readonly FileSystemWatcher watcher = null;
        private readonly UniversalLogger logger = null;
        private static Dictionary<string, DateTime> fileStatus = new Dictionary<string, DateTime>();

        public event FileChangeHandler FileChangeEvent;

        public FileWatcher(string targetDir, UniversalLogger logger)
        {
            watcher = new FileSystemWatcher();
            this.logger = logger;
            SetTarget(targetDir);
        }

        ~FileWatcher() 
        {
            StopWatcher();
        }

        public void SetTarget(string targetDir)
        {
            this.targetDir = targetDir;
            try
            {
                watcher.Path = targetDir;
            }catch (Exception ex)
            {
                logger.Log(LogType.LOCAL, "FileWatcher: 找不到目标文件夹");
            }
            logger.Log(LogType.LOCAL, "FileWatcher: 本地监听目标设置成功");
        }

        private string GetRelativePath(string fullPath)//获取相对路径
        {
            if (fullPath.StartsWith(this.targetDir))
            {
                return fullPath.Substring(this.targetDir.Length).TrimStart('\\');
            }
            else
            {
                return fullPath; // 如果路径不是以 targetDir 开头，返回原路径
            }
        }

        public void InformFileChange(ChangeType changeType, string fileDir)
        {
            FileChangeEvent.Invoke(new ChangeFileInfo(changeType, fileDir));
        }

        public void StartWatcher()
        {
            // 通知过滤器，设置需要监控的事件类型
            watcher.NotifyFilter = NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.LastWrite;

            // 注册事件处理器
            watcher.Created += OnFileCreated;
            watcher.Deleted += OnFileDeleted;
            watcher.Changed += OnFileChanged;
            watcher.Renamed += OnFileRenamed;

            // 开始监控
            watcher.EnableRaisingEvents = true;
            watcher.IncludeSubdirectories = true;

            logger.Log(LogType.LOCAL, "开始对本地监听");
        }
        
        public void StopWatcher()
        {
            // 停止监控
            watcher.EnableRaisingEvents = false;

            logger.Log(LogType.LOCAL, "停止对本地监听");
        }

        private void OnFileCreated(object source, FileSystemEventArgs e)
        {
            // 过滤
            if (IsTemporaryFile(e.FullPath) || e.ChangeType == WatcherChangeTypes.Deleted)
            {
                return;
            }

            // 添加的项目是文件夹
            if (Directory.Exists(e.FullPath))
            {
                logger.Log(LogType.LOCAL, $"已创建文件夹: {e.FullPath} ");
            }

            // 添加的是文件而非文件夹
            else
            {
                logger.Log(LogType.LOCAL, $"已创建文件: {e.FullPath} ");
            }

            // 记录文件创建的时间
            fileStatus[e.FullPath] = File.GetLastWriteTime(e.FullPath);
            string relativePath = GetRelativePath(e.FullPath);
            InformFileChange(ChangeType.CREATE, relativePath);
        }

        private void OnFileDeleted(object source, FileSystemEventArgs e)
        {
            // 过滤
            if (IsTemporaryFile(e.FullPath) || e.ChangeType == WatcherChangeTypes.Created)
            {
                return;
            }

            // 删除的是文件夹
            if (Directory.Exists(e.FullPath))
            {
                logger.Log(LogType.LOCAL, $"已删除文件夹: {e.FullPath} ");
            }

            // 删除的是文件
            else
            {
                logger.Log(LogType.LOCAL, $"已删除文件: {e.FullPath} ");
            }
            string relativePath = GetRelativePath(e.FullPath);
            InformFileChange(ChangeType.DELETE, relativePath);
        }

        private void OnFileChanged(object source, FileSystemEventArgs e)
        {
            // 过滤
            if (IsTemporaryFile(e.FullPath) || 
                e.ChangeType == WatcherChangeTypes.Created || 
                e.ChangeType == WatcherChangeTypes.Deleted)
            {
                return;
            }

            // 当文件夹中的文件有变动时，文件夹亦会触发Change事件
            // 所以需要过滤掉这种情况
            FileAttributes attr = File.GetAttributes(e.FullPath);
            if ((attr & FileAttributes.Directory) != FileAttributes.Directory)
            {
                logger.Log(LogType.LOCAL, $"已修改文件: {e.FullPath}");
            }
            string relativePath = GetRelativePath(e.FullPath);
            InformFileChange(ChangeType.MODIFY, relativePath);
        }

        private void OnFileRenamed(object source, RenamedEventArgs e)
        {
            // 过滤
            if (IsTemporaryFile(e.FullPath) || e.ChangeType == WatcherChangeTypes.Changed)
            {
                return;
            }

            // 重命名的对象为文件夹
            if (Directory.Exists(e.FullPath))
            {
                logger.Log(LogType.LOCAL, $"重命名文件夹: {e.OldFullPath} 为 {e.FullPath}");
                
            }

            // 重命名的对象为文件
            else
            {
                //因为在dox，doxc，xlx等文件建立重命名机制，对临时文件进行判断是否修改
                if (IsTemporaryFile(e.OldFullPath))
                {
                    logger.Log(LogType.LOCAL, $"已修改文件: {e.FullPath}");
                    InformFileChange(ChangeType.MODIFY, e.FullPath);
                    return;
                }
                else
                {
                    logger.Log(LogType.LOCAL, $"已重命名文件: {e.OldFullPath} 为 {e.FullPath}.");
                }
            }
            string OldrelativePath = GetRelativePath(e.OldFullPath);
            string relativePath = GetRelativePath(e.FullPath);
            InformFileChange(ChangeType.DELETE, OldrelativePath);
            InformFileChange(ChangeType.CREATE, relativePath);
        }

        // 判断是否为临时文件
        private bool IsTemporaryFile(string path)
        {
            string extension = Path.GetExtension(path).ToLower();
            return extension == ".tmp" || 
                   Path.GetFileName(path).StartsWith("~$") || 
                   extension == ".temp" ||
                   extension== ".TMP";
        }
        
    }

}
